package org.example.utils;

import com.alibaba.fastjson.JSON;
import org.apache.commons.lang3.StringUtils;
import org.example.annotation.NoSign;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Field;
import java.util.*;

/**
 * Description:签名工具类，可以对对象、二进制数据、字符串做签名
 */
@SuppressWarnings("all")
public class SignatureUtils {
    private static Logger logger = LoggerFactory.getLogger(SignatureUtils.class);

    //HASH算法
    public static final String HASH_TYPE_MD5 = "MD5";
    public static final String HASH_TYPE_SHA1 = "SHA1";

    //常量字符串
    private static final String AND = "&";
    private static final String EQUAL = "=";
    private static final String NULL_STRING = "";
    private static final String DEFAULT_KEY_PARAM_NAME = "key";

    public static final int CASE_TYPE_UPPER = 1;
    public static final int CASE_TYPE_LOWER = 2;

    /**
     * 验证签名
     * @param bean
     * @param signStr
     * @param key
     * @param keyName
     * @param hashType
     * @return
     */
    public static boolean doCheck(Object bean,String signStr,String key,String keyName,String hashType){
        String beanSign = "";
        try {
            beanSign = generateSignString(bean, key,keyName, hashType);
        }catch(Exception e){
            logger.info("生成签名串异常{}",e);
        }
        return beanSign.equalsIgnoreCase(signStr);
    }

    /**
     * 验证签名
     * @param bean
     * @param signStr
     * @param key
     * @param hashType
     * @return
     */
    public static boolean doCheck(Object bean,String signStr,String key,String hashType){
        String beanSign = "";
        try {
            beanSign = generateSignString(bean, key,null, hashType);
        }catch(Exception e){
            logger.info("生成签名串异常{}",e);
        }
        return beanSign.equalsIgnoreCase(signStr);
    }

    /**
     * 生成摘要签名
     *
     * @param bean
     * @param key
     * @param hashType
     * @return
     * @throws Exception
     */
    public static String generateSignString(Object bean, String key, String hashType) throws Exception {
        return generateSignString(bean,key,null,hashType,CASE_TYPE_LOWER);
    }

    /**
     * 生成摘要
      * @param bean
     * @param key
     * @param keyName
     * @param hashType
     * @return
     * @throws Exception
     */
    public static String generateSignString(Object bean, String key,String keyName, String hashType) throws Exception {
        return generateSignString(bean,key,keyName,hashType,CASE_TYPE_LOWER);
    }

    /**
     * 生成摘要
     * @param bean
     * @param key
     * @param hashType
     * @param caseType
     * @return
     * @throws Exception
     */
    public static String generateSignString(Object bean, String key, String hashType, int caseType) throws Exception {
        return generateSignString(bean,key,null,hashType,caseType);
    }
    /**
     * 生成摘要签名
     *
     * @param bean
     * @param publicKey
     * @param hashType
     * @param filterNames
     * @return
     * @throws Exception
     */
    public static String generateSignString(Object bean, String key,String keyName, String hashType, int caseType) throws Exception {
        Class cc = bean.getClass();
        List<Field> fieldList = new ArrayList<>();
        while(cc != null){
            Field[] fields = cc.getDeclaredFields();
            fieldList.addAll(Arrays.asList(fields));
            cc = cc.getSuperclass();
        }

        List<String> noSignFields = new ArrayList<>(fieldList.size());
        HashMap<String,String> beanMap = new HashMap<>(fieldList.size());
        for(Field field : fieldList){
            NoSign ns = field.getAnnotation(NoSign.class);
            String fieldName = field.getName();
            if(ns != null){
                continue;
            }
            field.setAccessible(true);
            Object ovalue = field.get(bean);
            if(ovalue == null){
                //空值忽略
                continue;
            }
            if(ovalue instanceof String){
                if (StringUtils.isBlank((String)ovalue)){
                    //空字符串忽略
                    continue;
                }else{
                    beanMap.put(fieldName,(String)ovalue);
                    continue;
                }
            }
            //其他非字符串字段
            if(ovalue != null){
                beanMap.put(fieldName, JSON.toJSONString(ovalue));
            }
        }
        return generateSignString(beanMap, key, keyName, hashType,caseType);
    }

    /**
     * 实现摘要签名的私有方法
     * @param props
     * @param publicKey
     * @param keyName
     * @param hashType
     * @return
     * @throws Exception
     */
    private static String generateSignString(Map<String, String> props, String publicKey , String keyName, String hashType, int caseType) throws Exception {
        StringBuilder sb = new StringBuilder();
        List<String> keys = new ArrayList<String>(props.keySet());
        Collections.sort(keys);

        for (int i = 0; i < keys.size(); i++) {
            String key = keys.get(i);
            Object value = props.get(key);
            sb.append(key).append(EQUAL).append(value).append(AND);
        }
        if (keyName != null) {
            sb.append(keyName).append("=").append(publicKey);
        } else {
            sb.append(DEFAULT_KEY_PARAM_NAME).append("=").append(publicKey);
        }
        logger.info("签名串：",sb);
        System.out.println("签名串：{}"+ sb);
        String signStr = "";
        if (HASH_TYPE_MD5.equalsIgnoreCase(hashType)) {
            //使用MD5摘要算法验证签名
            signStr = MessageDigestUtils.md5Hex(sb.toString());
        } else if (HASH_TYPE_SHA1.equalsIgnoreCase(hashType)) {
            //使用SH1摘要算法验证签名
            signStr = MessageDigestUtils.sha1Hex(sb.toString());
        } else {
            throw new Exception("不支持的签名算法");
        }
        if(caseType == CASE_TYPE_LOWER){
            signStr = signStr.toLowerCase();
        }else{
            signStr = signStr.toUpperCase();
        }
        return signStr;
    }

}
